#include "graph.h"
#include <node.h>
#include <edge.h>
#include "attributeset.h"

Graph::Graph(QGraphicsWidget *p)
    : QGraphicsObject(p), parent(p)
{
    vtkGraph = vtkSmartPointer<vtkMutableDirectedGraph>::New();
    setFlag( QGraphicsItem::ItemClipsChildrenToShape );
}

Graph::~Graph()
{
    for(int i = 0; i < edges.size() ; i++)
    {
        if( edges[i] )
            delete edges[i];
    }
    for(int i = 0; i < nodes.size() ; i++)
    {
        if( nodes[i] )
            delete nodes[i];
    }

}

Graph::Graph( const Graph& copy )
     : QGraphicsObject(copy.parent), parent(copy.parent)
{
    //copy edges
    for( int i = 0; i < copy.edges.size(); i++)
    {
        QString node1Name = edges[i]->getSourceNode()->getName();
        QString node2Name = edges[i]->getDestNode()->getName();

        addEdge( node1Name, node2Name, "unknown");//why empty string?  interaction type not stored in edge
    }

//    //position nodes
//    for( int i = 0; i < copy.nodes.size(); i++)
//    {
//        nodes[i]->
//    }

}

QRectF Graph::boundingRect() const
 {
     QRectF pRect = parent->boundingRect();
     pRect.adjust(10, 10, -200, -10);
     return pRect;
 }

void Graph::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    QColor background(0, 0, 0, 255 );
    painter->setBrush(background);
    painter->drawRect(boundingRect());
}

bool Graph::addEdge(QString node1Name, QString node2Name, QString interactionType)
{
    qDebug() << node1Name << " " << interactionType << node2Name << endl;

    //NODE 1- retrieve it if it has been encountered, add it if it hasn't
    Node* n1;
    int n1Idx = nodes.size();//this is the node idx if node has not been previously encountered
    if( nodeNameToNodeIdx.contains( node1Name ))
    {
        n1Idx = nodeNameToNodeIdx.value(node1Name) ;//if already encountered, get idx
        n1 = nodes[ n1Idx ];//and get node
    }
    else
    {
        n1 = new Node( node1Name, this );//make new node
        nodeNameToNodeIdx.insert( node1Name, nodes.size() );//add it to hash table
        nodes.push_back( n1 );//add it to vector of nodes
        vtkGraph->AddVertex();//add vertex to vtkGraph
    }

    //NODE 2- retrieve it if it has been encountered, add it if it hasn't
    Node* n2;
    int n2Idx = nodes.size();//this is the node idx if node has not been previously encountered
    if( nodeNameToNodeIdx.contains( node2Name ))
    {
        n2Idx = nodeNameToNodeIdx.value(node2Name) ;//if already encountered, get idx
        n2 = nodes[ n2Idx ];//and get node
    }
    else
    {
        n2 = new Node( node2Name, this );//make new node
        nodeNameToNodeIdx.insert( node2Name, nodes.size() );//add it to hash table
        nodes.push_back( n2 );//add it to vector of nodes
        vtkGraph->AddVertex();//add vertex to vtkGraph
    }

    //get or add edge
    QString edgeName( node1Name.append(node2Name) ); //THIS WOULD NEED TO CHANGE IN UNDIRECTED GRAPHS
    Edge* e;
    if( !edgeNameToEdgeIdx.contains( edgeName ))
    {
        qDebug() << "adding edge: " << node1Name << " to " << node2Name << endl;
        e = new Edge( n1, n2, this );
        edgeNameToEdgeIdx.insert( edgeName, nodes.size() );
        edges.push_back( e );
        vtkGraph->AddEdge( n1Idx, n2Idx);//add edge to graph
    }
    else
        return false;

    return true;
}

void Graph::itemMoved()
{
    //if (!timerId)
    //    timerId = startTimer(1000 / 25);
}

void Graph::applyForceDirectedLayout()
{
    //setup vtk layout objects
    vtkSmartPointer<vtkForceDirectedLayoutStrategy> force =
    vtkSmartPointer<vtkForceDirectedLayoutStrategy>::New();
    force->ThreeDimensionalLayoutOff();//laying out in 2d
    QRectF bounds = boundingRect();//bound graph by bounding rect
    force->SetGraphBounds(bounds.x()+20, bounds.x()+bounds.width()-20, bounds.y()+20, bounds.y()+bounds.height()-20, 0.0, 0.0);//25.0, bounds.width()-25.0, 25.0, bounds.height()-25.0, 0.0, 0.0);
    force->SetCoolDownRate(1.0);//high cool down means longer calculation times,
    force->RandomInitialPointsOn();

    vtkSmartPointer<vtkGraphLayout> layout =
    vtkSmartPointer<vtkGraphLayout>::New();
    layout->SetInput(vtkGraph);//vtkgraph object created in initialization applied to layout
    layout->SetLayoutStrategy(force);
    layout->Update();//should complete layout- doesn't always appear to or high cool-down rates

    //this is what is done for iterative layout- but google it for more accurate info
    //while( !layout->IsLayoutComplete() )
    //{
        //layout->Layout();
    //    qDebug() << "layout" << endl;
    //}

    //iterate through points in graph and apply layout
    vtkSmartPointer<vtkVertexListIterator> itr =
    vtkSmartPointer<vtkVertexListIterator>::New();
    layout->GetOutput()->GetVertices(itr);

    double location[3];//will hold vertex locations
    int numVertices = 0;//keeps count of vertices
    while (itr->HasNext())
    {
        layout->GetOutput()->GetPoint(numVertices, location);//gets location of node after layout

        if( numVertices >= nodes.size() )//check
        {
            qDebug() << "error too many vtk nodes" << endl;
            break;
        }

        //qDebug() << "moving node " << nodes[numVertices]->getName() << " from " << nodes[numVertices]->pos() << " to ";
        nodes[numVertices]->setPos(location[0], location[1]);//setpos is rel to parent
        //qDebug() << nodes[numVertices]->pos() << endl;

        itr->Next();//move to next vertex
        ++numVertices;
    }

}

//
void Graph::forgeConnection(Graph *other)
{
    if( this->nodes.size() < other->nodes.size() )
    {
        for(int i = 0 ; i < nodes.size(); i++)
        {
            QString nodeName = nodes[i]->getName(); //get all the names
            other->addConnection( nodeName, nodes[i] );//make connection between nodes
        }
    }
    else
    {
        for(int i = 0 ; i < other->nodes.size(); i++)
        {
            QString nodeName = other->nodes[i]->getName(); //get all the names
            addConnection( nodeName, other->nodes[i] );//make connection between nodes
        }
    }
}


//void Graph::forgeConnection(BioTableWidget *table)
//{
//    for(int i = 0 ; i < table->rowCount(); i++)
//    {
//        QString geneName = table->getGeneName();
//        QStringList geneAttributes = table->getGeneAttributes();
//        addAttribute( geneName, geneAttributes  );//make connection between nodes
//    }
//}


void Graph::addAttribute( QPointer<QualAttribute> att)
{
    QString nodeName = att->getDataValue();
    Node* n1;
    int n1Idx;
    if( nodeNameToNodeIdx.contains( nodeName ))
    {
        n1Idx = nodeNameToNodeIdx.value(nodeName) ;//if already encountered, get idx
        n1 = nodes[ n1Idx ];//and get node
        n1->addAttribute( att );
    }
}

void Graph::addConnection(QString nodeName, Node *node)
{
    Node* n1;
    int n1Idx;
    if( nodeNameToNodeIdx.contains( nodeName ))
    {
        n1Idx = nodeNameToNodeIdx.value(nodeName) ;//if already encountered, get idx
        n1 = nodes[ n1Idx ];//and get node

        n1->setPos( node->pos() );//place nodes in the same positions rel to parents

        //connect hover illumination
        connect( node, SIGNAL(hoverEnterSignal()), n1, SLOT(hoverEnterSlot()) );
        connect( n1, SIGNAL(hoverEnterSignal()), node, SLOT(hoverEnterSlot()) );
        connect( node, SIGNAL(hoverLeaveSignal()), n1, SLOT(hoverLeaveSlot()) );
        connect( n1, SIGNAL(hoverLeaveSignal()), node, SLOT(hoverLeaveSlot()) );

        //connect movement
        connect( node, SIGNAL(nodeMoved(QPointF)), n1, SLOT(moveNode(QPointF )) );
        connect( n1, SIGNAL(nodeMoved(QPointF)), node, SLOT(moveNode(QPointF )) );

        qDebug() << "connecting " << node->getName() << " with " << n1->getName() << endl;
    }
}

